﻿using System;
using System.Collections.Generic;
using Behaviorize.TextFormatting;

namespace Behaviorize
{
	internal class ScenarioConsoleWriter
	{
		private readonly Feature _feature;
		private readonly string _scenarioName;
		private readonly Queue<ScenarioVersion> _scenarios;

		internal ScenarioConsoleWriter(Feature feature, string scenarioName)
		{
			_feature = feature;
			_scenarioName = scenarioName;
			_scenarios = new Queue<ScenarioVersion>();
		}

		internal void AddScenario(ScenarioVersion scenarioVersion)
		{
			_scenarios.Enqueue(scenarioVersion);
		}

		private static void PrintStep(Step step, string prefix, int errorIndexAddend)
		{
			string message = prefix + TextFormatter.ToText(step.StepDelegate.Method.Name, true, step.Arguments);
			var lines = new List<string>();

			while (message.Length > 40)
			{
				int breakIndex = message.LastIndexOf(' ', 40);
				lines.Add(message.Substring(0, breakIndex).Trim());
				message = message.Substring(breakIndex + 1);
			}

			lines.Add(message);

			for (int i = 0; i < lines.Count; i++)
			{
				string line = i > 0 ? "  " + lines[i] : lines[i];

				if (i < lines.Count - 1)
				{
					Console.WriteLine(line);
				}
				else
				{
					string failureIndex = step.FailureIndex > 0 ? string.Format(" ({0})", step.FailureIndex + errorIndexAddend) : "";
					string result = step.Outcome != OutcomeType.Passed ? string.Format("<< {0}{1} >>", step.Outcome.ToString().ToLowerInvariant(), failureIndex) : "";
					Console.WriteLine("{0,-45}{1}", line, result);
				}
			}
		}

		private void PrintSteps<T>(List<T> steps, int errorIndexAddend) where T : Step
		{
			for (int i = 0; i < steps.Count; i++)
			{
				Step step = steps[i];
				PrintStep(step, i == 0 ? step.ClauseName : "and", errorIndexAddend);
			}
		}

		private IEnumerable<Exception> PrintScenario(ScenarioVersion scenarioVersion, int errorIndexAddend)
		{
			PrintSteps(scenarioVersion.ArrangementSteps, errorIndexAddend);
			PrintSteps(scenarioVersion.ActionSteps, errorIndexAddend);
			PrintSteps(scenarioVersion.AssertionSteps, errorIndexAddend);

			Console.WriteLine();

			return scenarioVersion.GetExceptions();
		}

		internal void PrintToConsole()
		{
			Console.WriteLine("Feature: " + _feature.Name);
			Console.WriteLine();

			if (_feature.UserStory != null)
			{
				Console.WriteLine("As a {0},", _feature.UserStory.Role);
				Console.WriteLine("I want {0}", _feature.UserStory.Goal);
				Console.WriteLine("so that {0}", _feature.UserStory.Motivation);
				Console.WriteLine();
			}

			if (!String.IsNullOrEmpty(_feature.Description))
			{
				Console.WriteLine("Description: " + _feature.Description);
				Console.WriteLine();
			}

			Console.WriteLine("--");
			Console.WriteLine();
			Console.WriteLine("Scenario: " + _scenarioName);
			Console.WriteLine();

			var errors = new List<Exception>();

			while (_scenarios.Count > 0)
			{
				ScenarioVersion scenarioVersion = _scenarios.Dequeue();
				errors.AddRange(PrintScenario(scenarioVersion, errors.Count));
			}

			if (errors.Count > 0)
			{
				Console.Write("\nFailures:\n");
				for (int i = 0; i < errors.Count; i++)
				{
					Exception exception = errors[i];
					Console.WriteLine("{0}: {1}{2}", i + 1, exception.Message, i == 0 ? " (see stack trace)" : "");
				}
			}
		}
	}
}